package com.ittao.taoaicodecommon.manage.storage.resource;

import cn.hutool.core.io.FileUtil;
import com.ittao.taoaicodecommon.config.AppStorageConfig;
import com.ittao.taoaicodecommon.manage.storage.enums.StorageType;
import com.ittao.taoaicodecommon.manage.storage.factory.FileStorageStrategyFactory;
import com.ittao.taoaicodecommon.manage.storage.service.TempDirectoryService;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.io.File;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 资源保护管理器
 * 负责监控系统资源使用情况，防止资源耗尽
 * 特别针对2核2GB服务器环境进行优化
 * <p>
 * 注意：此类只负责资源监控和临时文件清理，不处理文件存储操作
 * - local策略模式：不执行任何临时文件操作
 * - COS策略模式：执行临时文件清理和资源监控
 */
@Slf4j
@Component
public class ResourceProtectionManager {

    @Resource
    private AppStorageConfig appStorageConfig;

    private final AtomicLong totalCleanedSize = new AtomicLong(0);
    private final AtomicLong totalCleanedFiles = new AtomicLong(0);
    private final MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();

    @Resource
    private FileStorageStrategyFactory fileStorageStrategyFactory;

    @Resource
    private TempDirectoryService tempDirectoryService;

    @PostConstruct
    public void init() {
        // 添加JVM关闭钩子
        Runtime.getRuntime().addShutdownHook(new Thread(this::emergencyCleanup, "resource-cleanup-shutdown"));

        // 启动时执行智能清理（仅COS模式）
        performStartupCleanup();

        log.info("资源保护管理器已启用");
    }

    @PreDestroy
    public void destroy() {
        log.info("资源保护管理器正在关闭...");
        emergencyCleanup();
    }

    /**
     * 启动时执行智能清理
     * 只有COS存储模式才执行临时目录清理
     */
    private void performStartupCleanup() {
        // 只有COS存储模式才需要清理临时目录
        if (fileStorageStrategyFactory.getCurrentStrategy().getStorageType() != StorageType.COS) {
            log.debug("当前为local存储模式，跳过启动清理");
            return;
        }

        try {
            log.info("=== 启动时COS临时目录清理开始 ===");

            // 构建实际的临时目录路径，与CosFileStorageStrategy保持一致
            String actualTempDirPath = System.getProperty("user.dir") + appStorageConfig.getCos().getTempDirPath();
            Path tempDir = Paths.get(actualTempDirPath);
            if (!Files.exists(tempDir)) {
                log.info("临时目录不存在，无需清理: {}", tempDir);
                return;
            }

            // 获取清理阈值时间
            long cleanupThreshold = System.currentTimeMillis() - (appStorageConfig.getCos().getCleanupDelaySeconds() * 1000L);

            // 扫描用户目录
            File[] userDirs = tempDir.toFile().listFiles(File::isDirectory);
            if (userDirs == null || userDirs.length == 0) {
                log.info("临时目录为空，无需清理");
                return;
            }

            int totalProjectDirs = 0;
            int cleanedProjectDirs = 0;
            int expiredProjectDirs = 0;
            int recentProjectDirs = 0;
            long cleanedSize = 0;

            log.info("开始清理临时目录，用户目录数: {}, 清理阈值: {}分钟前",
                    userDirs.length, appStorageConfig.getCos().getCleanupDelaySeconds() / 60);

            // 遍历每个用户目录
            for (File userDir : userDirs) {
                if (!userDir.isDirectory()) {
                    continue;
                }

                // 检查用户目录下的code_output目录
                File codeOutputDir = new File(userDir, "code_output");
                if (codeOutputDir.exists() && codeOutputDir.isDirectory()) {
                    // 扫描项目目录（vue_project_*, html_*, multi_file_*）
                    File[] projectDirs = codeOutputDir.listFiles(File::isDirectory);
                    if (projectDirs != null && projectDirs.length > 0) {
                        // 收集需要批量删除的过期目录
                        List<Path> expiredDirectories = new ArrayList<>();
                        
                        for (File projectDir : projectDirs) {
                            totalProjectDirs++;
                            
                            // 获取项目目录的实际最后修改时间（递归检查所有文件）
                            long actualLastModified = getActualLastModifiedTime(projectDir);
                            long dirSize = calculateDirectorySize(projectDir);

                            if (actualLastModified < cleanupThreshold) {
                                // 过期项目目录，加入批量删除列表
                                expiredProjectDirs++;
                                
                                // 安全检查：确保不是正在使用的目录
                                if (isSafeToDelete(projectDir)) {
                                    expiredDirectories.add(projectDir.toPath());
                                    cleanedSize += dirSize;
                                    log.debug("标记过期项目目录待删除: {} ({}MB, 最后修改: {}分钟前)",
                                            projectDir.getAbsolutePath(),
                                            dirSize / 1024 / 1024,
                                            (System.currentTimeMillis() - actualLastModified) / 60000);
                                } else {
                                    log.warn("项目目录可能正在使用，跳过清理: {}", projectDir.getAbsolutePath());
                                }
                            } else {
                                // 最近的项目目录，保留
                                recentProjectDirs++;
                                log.debug("保留最近项目目录: {} ({}MB, 最后修改: {}分钟前)",
                                        projectDir.getName(),
                                        dirSize / 1024 / 1024,
                                        (System.currentTimeMillis() - actualLastModified) / 60000);
                            }
                        }
                        
                        // 批量删除过期目录（优化性能）
                        if (!expiredDirectories.isEmpty()) {
                            log.info("开始批量删除 {} 个过期项目目录", expiredDirectories.size());
                            int deletedCount = tempDirectoryService.batchDeleteDirectories(expiredDirectories);
                            cleanedProjectDirs += deletedCount;
                            log.info("批量删除完成: 成功删除 {}/{} 个过期项目目录", deletedCount, expiredDirectories.size());
                        }
                    }

                    // 清理空的code_output目录
                    if (codeOutputDir.listFiles() == null || codeOutputDir.listFiles().length == 0) {
                        if (FileUtil.del(codeOutputDir)) {
                            log.debug("清理空的code_output目录: {}", codeOutputDir.getAbsolutePath());
                        }
                    }
                }

                // 检查用户目录下的code_deploy目录
                File codeDeployDir = new File(userDir, "code_deploy");
                if (codeDeployDir.exists() && codeDeployDir.isDirectory()) {
                    // 扫描部署目录（按deployKey分组）
                    File[] deployDirs = codeDeployDir.listFiles(File::isDirectory);
                    if (deployDirs != null && deployDirs.length > 0) {
                        // 收集需要批量删除的过期部署目录
                        List<Path> expiredDeployDirectories = new ArrayList<>();
                        
                        for (File deployDir : deployDirs) {
                            totalProjectDirs++;
                            
                            // 获取部署目录的实际最后修改时间（递归检查所有文件）
                            long actualLastModified = getActualLastModifiedTime(deployDir);
                            long dirSize = calculateDirectorySize(deployDir);

                            if (actualLastModified < cleanupThreshold) {
                                // 过期部署目录，加入批量删除列表
                                expiredProjectDirs++;
                                
                                // 安全检查：确保不是正在使用的目录
                                if (isSafeToDelete(deployDir)) {
                                    expiredDeployDirectories.add(deployDir.toPath());
                                    cleanedSize += dirSize;
                                    log.debug("标记过期部署目录待删除: {} ({}MB, 最后修改: {}分钟前)",
                                            deployDir.getAbsolutePath(),
                                            dirSize / 1024 / 1024,
                                            (System.currentTimeMillis() - actualLastModified) / 60000);
                                } else {
                                    log.warn("部署目录可能正在使用，跳过清理: {}", deployDir.getAbsolutePath());
                                }
                            } else {
                                // 最近的部署目录，保留
                                recentProjectDirs++;
                                log.debug("保留最近部署目录: {} ({}MB, 最后修改: {}分钟前)",
                                        deployDir.getName(),
                                        dirSize / 1024 / 1024,
                                        (System.currentTimeMillis() - actualLastModified) / 60000);
                            }
                        }
                        
                        // 批量删除过期部署目录（优化性能）
                        if (!expiredDeployDirectories.isEmpty()) {
                            log.info("开始批量删除 {} 个过期部署目录", expiredDeployDirectories.size());
                            int deletedCount = tempDirectoryService.batchDeleteDirectories(expiredDeployDirectories);
                            cleanedProjectDirs += deletedCount;
                            log.info("批量删除完成: 成功删除 {}/{} 个过期部署目录", deletedCount, expiredDeployDirectories.size());
                        }
                    }

                    // 清理空的code_deploy目录
                    if (codeDeployDir.listFiles() == null || codeDeployDir.listFiles().length == 0) {
                        if (FileUtil.del(codeDeployDir)) {
                            log.debug("清理空的code_deploy目录: {}", codeDeployDir.getAbsolutePath());
                        }
                    }
                }

                // 清理空的用户目录
                if (userDir.listFiles() == null || userDir.listFiles().length == 0) {
                    if (FileUtil.del(userDir)) {
                        log.debug("清理空的用户目录: {}", userDir.getAbsolutePath());
                    }
                }
            }

            // 更新统计信息
            if (cleanedProjectDirs > 0) {
                totalCleanedFiles.addAndGet(cleanedProjectDirs);
                totalCleanedSize.addAndGet(cleanedSize);
            }

            // 检查清理后的磁盘空间
            long totalSizeAfter = calculateDirectorySize(tempDir.toFile());
            long remainingProjectDirs = totalProjectDirs - cleanedProjectDirs;

            log.info("=== COS临时目录清理完成 ===");
            log.info("清理统计: 总项目目录数={}, 过期项目数={}, 最近项目数={}", totalProjectDirs, expiredProjectDirs, recentProjectDirs);
            log.info("清理结果: 成功清理{}个项目目录，释放{}MB空间", cleanedProjectDirs, cleanedSize / 1024 / 1024);
            log.info("剩余状态: {}个项目目录，占用{}MB空间", remainingProjectDirs, totalSizeAfter / 1024 / 1024);

            // 如果清理后空间仍然紧张，记录警告
            long maxSize = appStorageConfig.getCos().getTempDirMaxSize();
            double usageRatio = (double) totalSizeAfter / maxSize;
            if (usageRatio > 0.8) {
                log.warn("启动清理后磁盘使用率仍然较高: {}% ({}/{}MB)",
                        usageRatio * 100, totalSizeAfter / 1024 / 1024, maxSize / 1024 / 1024);
            } else {
                log.info("启动清理后磁盘使用率: {}% ({}/{}MB)",
                        usageRatio * 100, totalSizeAfter / 1024 / 1024, maxSize / 1024 / 1024);
            }

        } catch (Exception e) {
            log.error("COS临时目录清理失败", e);
        }
    }

    /**
     * 获取目录的实际最后修改时间（递归检查所有文件）
     * @param directory 目录
     * @return 最新的文件修改时间
     */
    private long getActualLastModifiedTime(File directory) {
        if (!directory.exists() || !directory.isDirectory()) {
            return directory.lastModified();
        }

        long latestTime = directory.lastModified();
        
        try {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        // 递归检查子目录
                        long subDirTime = getActualLastModifiedTime(file);
                        latestTime = Math.max(latestTime, subDirTime);
                    } else {
                        // 检查文件修改时间
                        latestTime = Math.max(latestTime, file.lastModified());
                    }
                }
            }
        } catch (Exception e) {
            log.warn("获取目录实际修改时间失败: {}", directory.getAbsolutePath(), e);
        }

        return latestTime;
    }

    /**
     * 检查目录是否可以安全删除
     * @param directory 目录
     * @return 是否可以安全删除
     */
    private boolean isSafeToDelete(File directory) {
        try {
            // 检查是否有进程正在使用该目录
            // 简单的检查方法：尝试重命名目录，如果失败说明可能被占用
            File tempName = new File(directory.getParent(), directory.getName() + ".tmp_check");
            if (directory.renameTo(tempName)) {
                // 重命名成功，恢复原名
                tempName.renameTo(directory);
                return true;
            } else {
                log.warn("目录可能被占用，无法重命名: {}", directory.getAbsolutePath());
                return false;
            }
        } catch (Exception e) {
            log.warn("安全检查失败，谨慎起见不删除目录: {}", directory.getAbsolutePath(), e);
            return false;
        }
    }

    // === 资源监控方法 ===

    /**
     * 定时检查资源使用情况
     * 每30秒执行一次
     */
    @Scheduled(fixedRate = 30000)
    public void checkResourceUsage() {
        try {
            checkMemoryUsage();
            checkTempDirectorySize();
        } catch (Exception e) {
            log.error("资源使用检查失败", e);
        }
    }

    /**
     * 检查内存使用情况
     */
    private void checkMemoryUsage() {
        MemoryUsage heapMemory = memoryBean.getHeapMemoryUsage();
        long used = heapMemory.getUsed();
        long max = heapMemory.getMax();
        double usageRatio = (double) used / max;

        if (used > appStorageConfig.getCos().getMemoryThreshold()) {
            log.warn("内存使用超过阈值: {}MB / {}MB ({}%)",
                    used / 1024 / 1024, max / 1024 / 1024, String.format("%.1f", usageRatio * 100));

            if (usageRatio > appStorageConfig.getCos().getGcThreshold()) {
                log.warn("内存使用率过高，建议执行垃圾回收");
                System.gc();
            }
        }

        // 每5分钟记录一次内存状态
        if (System.currentTimeMillis() % 300000 < 30000) {
            log.debug("内存状态: {}MB / {}MB ({}%)",
                    used / 1024 / 1024, max / 1024 / 1024, String.format("%.1f", usageRatio * 100));
        }
    }

    /**
     * 检查临时目录大小（仅COS模式）
     */
    public void checkTempDirectorySize() {
        // 只有COS存储模式才需要检查临时目录
        if (fileStorageStrategyFactory.getCurrentStrategy().getStorageType() != StorageType.COS) {
            return;
        }

        try {
            Path tempPath = Paths.get(appStorageConfig.getCos().getTempDirPath());
            if (!Files.exists(tempPath)) {
                return;
            }

            long currentSize = calculateDirectorySize(tempPath.toFile());
            long maxSize = appStorageConfig.getCos().getTempDirMaxSize();
            double usageRatio = (double) currentSize / maxSize;

            if (currentSize > maxSize) {
                log.warn("临时目录大小超过限制: {}MB / {}MB ({}%)",
                        currentSize / 1024 / 1024, maxSize / 1024 / 1024, String.format("%.1f", usageRatio * 100));

                // 触发紧急清理
                if (appStorageConfig.getCos().isEnableEmergencyCleanup()) {
                    double bufferRatio = appStorageConfig.getCos().getEmergencyCleanupBufferRatio();
                    log.warn("触发紧急清理策略，目标清理比例: {}%", bufferRatio * 100);
                    forceCleanupOldestSessionsImmediately(bufferRatio);
                } else {
                    log.warn("紧急清理已禁用，请手动清理临时目录");
                }
            }
        } catch (Exception e) {
            log.error("检查临时目录大小失败", e);
        }
    }

    /**
     * 立即强制清理最旧的会话目录（紧急清理策略）
     */
    private void forceCleanupOldestSessionsImmediately(double bufferRatio) {
        try {
            Path tempPath = Paths.get(appStorageConfig.getCos().getTempDirPath());
            File tempDir = tempPath.toFile();
            File[] sessionDirs = tempDir.listFiles(File::isDirectory);

            if (sessionDirs == null || sessionDirs.length == 0) {
                log.info("临时目录为空，无需清理");
                return;
            }

            // 按修改时间排序，最旧的在前
            Arrays.sort(sessionDirs, Comparator.comparingLong(File::lastModified));

            long maxSize = appStorageConfig.getCos().getTempDirMaxSize();
            long targetCleanupSize = (long) (maxSize * bufferRatio);
            long cleanedSize = 0;
            int cleanedDirs = 0;

            log.info("开始紧急清理策略：目标清理大小{}MB，当前会话目录数量{}",
                    targetCleanupSize / 1024 / 1024, sessionDirs.length);

            for (File sessionDir : sessionDirs) {
                if (cleanedSize >= targetCleanupSize) {
                    break;
                }

                long dirSize = calculateDirectorySize(sessionDir);
                if (FileUtil.del(sessionDir)) {
                    cleanedDirs++;
                    cleanedSize += dirSize;
                    log.info("紧急清理会话目录: {} ({}MB)，修改时间: {}",
                            sessionDir.getName(),
                            dirSize / 1024 / 1024,
                            LocalDateTime.ofInstant(
                                    java.time.Instant.ofEpochMilli(sessionDir.lastModified()),
                                    java.time.ZoneId.systemDefault()
                            ).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
                    );
                } else {
                    log.warn("无法删除会话目录: {}", sessionDir.getName());
                }
            }

            if (cleanedDirs > 0) {
                totalCleanedFiles.addAndGet(cleanedDirs);
                totalCleanedSize.addAndGet(cleanedSize);
                log.warn("紧急清理策略执行完成: 清理{}个目录，释放{}MB空间，剩余会话目录{}个",
                        cleanedDirs, cleanedSize / 1024 / 1024, sessionDirs.length - cleanedDirs);
            } else {
                log.warn("紧急清理策略未能清理任何目录");
            }

        } catch (Exception e) {
            log.error("紧急清理策略执行失败", e);
        }
    }

    /**
     * 计算目录大小
     */
    private long calculateDirectorySize(File directory) {
        if (!directory.exists() || !directory.isDirectory()) {
            return 0;
        }

        long size = 0;
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    size += calculateDirectorySize(file);
                } else {
                    size += file.length();
                }
            }
        }
        return size;
    }

    /**
     * 紧急清理（JVM关闭时）
     */
    public void emergencyCleanup() {
        try {
            log.info("执行紧急资源清理...");

            // 只有COS模式才需要清理临时目录
            if (fileStorageStrategyFactory.getCurrentStrategy().getStorageType() == StorageType.COS) {
                Path tempPath = Paths.get(appStorageConfig.getCos().getTempDirPath());
                if (Files.exists(tempPath)) {
                    FileUtil.del(tempPath.toFile());
                    log.info("紧急清理完成: 删除临时目录 {}", appStorageConfig.getCos().getTempDirPath());
                }
            }

            // 输出统计信息
            log.info("资源保护管理器统计: 总共清理{}个目录，释放{}MB空间",
                    totalCleanedFiles.get(), totalCleanedSize.get() / 1024 / 1024);

        } catch (Exception e) {
            log.error("紧急清理失败", e);
        }
    }

}